﻿namespace SeedObject
{
    using Serializable = System.SerializableAttribute;
    using XmlSerializer = System.Xml.Serialization.XmlSerializer;
    using XmlRoot = System.Xml.Serialization.XmlRootAttribute;

    /// <summary>
    /// For some reason the generic dictionary. NET not Serializable to XML.
    /// The next extension makes implementing Serializable
    /// IXmlSerializable infrastructure methods.
    /// </summary>
    /// <typeparam name="TKey">type of key</typeparam>
    /// <typeparam name="TValue">type of value</typeparam>
    [
         Serializable
        , XmlRoot("Recipient")
    ]
    public class Recipient<T> : Seed
        where T : ISeed
    {
        #region Fields
        System.Collections.Generic.IList<T> NumericIndexer =
            new System.Collections.Generic.List<T>();
        #endregion
        #region Content

        private System.Collections.Generic.Dictionary<string, T> _Content =
            new System.Collections.Generic.Dictionary<string, T>();
        public System.Collections.Generic.Dictionary<string, T> Content
        {
            get { return _Content; }
        }
        #endregion
        #region Add
        public void Add(Recipient<T> rt)
        {
            rt.ForEach(
                delegate(T obj)
                {
                    Add(obj);
                }
            );
        }
        public void Add(params T[] objs)
        {
            for (int i = 0; i < objs.Length; i++)
                Add(objs[i]);
        }
        internal virtual void Add(T obj)
        {
            string Id = obj.Id;

            if (_Content.ContainsKey(Id)) return;

            // is adding programmatically 
            if (Seed.Busy(this))
            {
                Action RestoreAddItem = delegate()
                {
                    _Content.Remove(Id);
                    NumericIndexer.Remove(obj);
                };
                Restorers.Add(delegate()
                {
                    Restorers.Remove(RestoreAddItem);
                });
                Restorers.Add(RestoreAddItem);
            }
            NumericIndexer.Add(obj);
            _Content.Add(Id, obj);
        }
        #endregion
        #region ContainsKey
        /// <summary>
        /// check if there is a content Item 
        /// </summary>
        /// <param name="id">object identifier</param>
        /// <returns>result of operation</returns>
        public bool ContainsKey(string id)
        {
            return _Content.ContainsKey(id);
        }
        #endregion
        #region Clear
        public void Clear()
        {
            _Content.Clear();
            NumericIndexer.Clear();
        }
        #endregion
        #region Indexer
        public virtual T this[string id]
        {
            get
            {
                return _Content[id];
            }
        }
        public T this[int id]
        {
            get
            {
                return NumericIndexer[id];
            }
        }
        #endregion
        #region ForEach
        public void ForEach(System.Action<T> action)
        {
            foreach (T seed in _Content.Values)
            {
                if (!seed.InA(State.Excluded))
                    action(seed);
            }
        }
        public System.Collections.Generic.IList<TResult>
            ForEach<TResult>(Action<TResult, T> action)
        {
            TResult ActionResult;
            // creates the list to return the results of the delegate
            System.Collections.Generic.IList<TResult> ListResult =
                new System.Collections.Generic.List<TResult>(_Content.Count);

            foreach (T seed in _Content.Values)
            {
                if (!seed.InA(State.Excluded))
                {
                    // invokes the delegate with the Item
                    ActionResult = action(seed);
                    // checks the result is not null
                    if (ActionResult != null)
                        // if the result is not null stores it in the list of return
                        ListResult.Add(ActionResult);
                }
            }
            return ListResult;
        }
        #endregion
        #region Remove
        /// <summary>
        /// allow delete an item from the container
        /// </summary>
        /// <param name="obj">object to remove</param>
        public virtual void Remove(string obj)
        {
            if (Seed.Busy(this))
            {
                T Item = _Content[obj];

                Action RestorRemoveEl = delegate()
                {
                    _Content.Add(obj, Item);
                    NumericIndexer.Add(Item);
                };
                Action RestorRestorers = delegate()
                {
                    Restorers.Remove(RestorRemoveEl);
                };
                Restorers.Add(RestorRestorers);
                Restorers.Add(RestorRemoveEl);
                _Content.Remove(obj);
                NumericIndexer.Remove(Item);
            }
        }
        #endregion
        #region Restore
        /// <summary>
        /// set the initial state
        /// </summary>  
        public override void Restore()
        {
            foreach (T seed in _Content.Values)
                seed.Restore();
            base.Restore();

        }
        #endregion

        #region IXmlSerializable Members


        public override void ReadXml(System.Xml.XmlReader reader)
        {
            XmlSerializer Serializer = new XmlSerializer(typeof(T));
            // through all the entries
            base.ReadXml(reader);
            while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
                Add((T)Serializer.Deserialize(reader));
            reader.ReadEndElement();
        }

        /// <summary>
        /// Write the contents of the container in xml format
        /// </summary>
        /// <param name="writer">xml writer</param>
        public override void WriteXml(System.Xml.XmlWriter writer)
        {
            XmlSerializer Serializer = new XmlSerializer(typeof(T));
            base.WriteXml(writer);
            foreach (T obj in _Content.Values)
                Serializer.Serialize(writer, obj);
        }
        #endregion
    }
    [Serializable]
    public class Conteiner<T> : Recipient<T>
        where T : ISeed
    {
        #region Add
        internal override void Add(T obj)
        {
            // adopts
            if (obj.Parent == null) obj.Parent = this;
            base.Add(obj);
        }
        #endregion
    }
}